/*******************************************************************
*
*    DESCRIPTION:implement the function to exec specify iptables command
*
*    AUTHOR:LiuHui
*
*    HISTORY:
*
*    DATE:8/31/2006
*
*******************************************************************/
#include "xmlutil.h"
#include "config.h"
#include "head.h"
#include "iptables.h"
//gobal arrary:app_conf,contain the app info from config file
struct AppEntry app_conf[MAX_APP];
struct ServiceEntry service_conf[MAX_APP];
struct DportEntry allowdport[MAX_APP];
int app_counter;
int service_counter;
int xml_app_counter;
int dport_counter = 0;
/*
 *Function Name:IptablesCheckListeningPort
 *
 *Parameters:
 *
 *Description:
 *
 *Returns:
 *
 */

int IptablesCheckListeningPort(int pid, int port[], int n)
{
	char cpath[256];
	memset(cpath, 0, 256);
	int inode[MAX_SOCKET];
	char buf[256];
	sprintf(cpath, "/proc/%d/fd/", pid); 
	int i = 0;
	int j = 0;
	int ret = 0;
	DIR *dirp;
	struct dirent *direntp = malloc(sizeof(struct dirent) + _POSIX_PATH_MAX);
	struct dirent *res = malloc(sizeof(struct dirent) + _POSIX_PATH_MAX);
	dirp = opendir(cpath);
	if ( dirp == NULL )
	{
		printf("In IptablesCheckListeningPort:can't open %s\n",cpath);
		return 0;
	}
	else
	{
		while ( (readdir_r(dirp, direntp, &res) == 0) && (res != NULL) )
		{
			memset(cpath, 0, 256);
			memset(buf, 0, 256);
			sprintf(cpath, "/proc/%d/fd/%s", pid, direntp->d_name);
			int result = readlink(cpath, buf, 255);
			if ( result == -1 )
			{
				continue;
			}
			else
			{
				if ( strstr(buf, "socket") != NULL )
				{
					inode[j] = atoi(buf + 8);
#ifdef _DEBUG
					printf("%s\n", buf);
#endif
					assert(inode[j] != 0);
					j ++;
				}
			}
		}

		FILE *fp;
		char tcpitem[256];
		memset(tcpitem, 0, 256);
		fp = fopen("/proc/net/tcp", "r");
		assert(fp != NULL);
		int k = 0;
		sleep(2);////????
		while ( fgets(tcpitem, 256, fp) != NULL )
		{
#ifdef _DEBUG
			printf("tcpitem in /proc/net/tcp:%s\n", tcpitem);
#endif
			if ( k == 0 )
			{
				k++;
				continue;//skip first line
			}
			else
			{
				char *mark = strnchr(tcpitem, ':', 3);
				assert(mark != NULL);
				char status[5];
				char cinode[10];
				char citem_inode[10];
				memset(status, 0, 5);
				strncpy(status, mark + 6, 2);//point to /proc/net/tcp "st" filed
				if ( strcmp(status, "0A") == 0 )//find listing item
				{
					for ( i = 0; i < j; i++ )
					{
						memset(cinode, 0, 10);
						memset(citem_inode, 0, 10);
						sprintf(cinode, "%d", inode[i]);
						char *mark1 = strchr(tcpitem + 91, ' ');
						assert(mark1 != NULL);
						char inode_sz = mark1 - (tcpitem + 91);
						strncpy(citem_inode, tcpitem + 91, inode_sz);//get inode from tcp item
#ifdef _DEBUG
						printf("In IptablesCheckListening:inode_form_file:%s, inode_from_array:%s\n", citem_inode, cinode);
#endif
						if ( strcmp(citem_inode, cinode) == 0 )	//if it's the app's item
						{
#ifdef _DEBUG
							printf("find listeing port\n");
#endif
							char hex_sport[6];
							memset(hex_sport, 0, 6);
							char *mark2 = strchr(tcpitem + 15, ' ');
							int sport_sz = mark2 - (tcpitem + 15);
							strncpy(hex_sport, tcpitem + 15, sport_sz);
							port[ret] = strtol(hex_sport, NULL, 16);
							ret++;
						}
					}


				}
			}
		}
		if ( ret <= n )
		{
			return ret;
		}
		else
		{
			printf("MARCO ERROR:to many listening port\n");
			return n;
		}
	}
}
/*
 *Function Name:IptablesOpenService
 *
 *Parameters:
 *
 *Description:
 *
 *Returns:
 *
 */
void IptablesOpenService(int service_sport)
{
	char cmd[256];
	memset(cmd, 0, 256);
	//OUTPUT
	sprintf(cmd, "iptables -I OUTPUT 4 -p tcp --sport %d -j ACCEPT", service_sport);
#ifdef _DEBUG
	printf("Dynamic add listening port:%s\n", cmd);
#endif
	system(cmd);
	//INPUT
	memset(cmd, 0, 256);
	sprintf(cmd, "iptables -I INPUT -p tcp --dport %d -j ACCEPT", service_sport);
#ifdef _DEBUG
	printf("Dynamic add listening port:%s\n", cmd);
#endif
	system(cmd);
}

/*
 *Function Name:IptablesCancelService
 *
 *Parameters:
 *
 *Description:
 *
 *Returns:
 *
 */
void IptablesCancelService(int listen_port)
{
	char cmd[256];
	memset(cmd, 0, 256);
	//OUTPUT
	sprintf(cmd, "iptables -D OUTPUT  -p tcp --sport %d -j ACCEPT", listen_port);
#ifdef _DEBUG
	printf("cancel listening port in OUTPUT:%s\n", cmd);
#endif
	system(cmd);
	//INPUT
	memset(cmd, 0, 256);
	sprintf(cmd, "iptables -D INPUT -p tcp --dport %d -j ACCEPT", listen_port);
#ifdef _DEBUG
	printf("cancel listening port in INPUT:%s\n", cmd);
#endif
	system(cmd);
}
/*
 *Function Name:IptablesInitByXml
 *
 *Parameters:
 *
 *Description:When start up, intialize iptables, After that:
 *
 *Returns:
 *
 */
void IptablesInitByXml()
{
	int i;
	//load modules
	system("modprobe iptable_filter");
	system("modprobe ip_queue");
	//flush OUTPUT chains and add two rules to queue tcp and udp packets
	system("iptables -F");
	//enable loopback
	system("iptables -A OUTPUT -o lo -j ACCEPT");
	system("iptables -A INPUT -i lo -j ACCEPT");
	system("iptables -A OUTPUT -p tcp  -j QUEUE");//queue tcp
	system("iptables -A OUTPUT -p udp  -j QUEUE");//queue udp
	//add state module to allow matched packet bypass the firewall
	system("iptables -I OUTPUT -m state --state ESTABLISHED -j ACCEPT");
	system("iptables -I INPUT -m state --state ESTABLISHED -j ACCEPT");
	//queue FIN package in OUTPUT, because INPUT chain may be flooded by scanner
	system("iptables -I OUTPUT -p tcp --tcp-flags FIN FIN -j QUEUE");
	//set INPUT default policy DROP
	system("iptables -P INPUT DROP");
	//read xml config file and initialize  (app_conf array and app_counter)
	InitAppConfArray(conf_xml);
	//InitServiceConfArray
	InitServiceConfArray(conf_xml);
	//init allowdport array
	for ( i = 0; i < MAX_APP; i++ )
	{
		allowdport[i].counter = 0;
	}

	for ( i = 0; i < service_counter; i++ )
	{
		if ( service_conf[i].allow == 1 )
		{
			IptablesOpenService(service_conf[i].sport);
		}
	}
}
/*
 *Function Name:AllowOldApp
 *
 *Parameters:
 *
 *Description:note:we should add another function:open listening port!!
 *
 *Returns:
 *
 */
void IptablesAllowOldApp(int sport, int dport ,int protocol, int pid, int index)
{
	int i;
	char cproto[10];
	memset(cproto, 0, 10);
	char iptablescmd[128];
	if ( protocol == 17 )
	{
		sprintf(cproto, "udp");
	}
	else if ( protocol == 6 )
	{
		sprintf(cproto, "tcp");
	}

	//add rules on OUTPUT
	memset(iptablescmd, 0, 128);
	//the first three rules are:FIN-state-lo
	sprintf(iptablescmd, "iptables -I OUTPUT 4 -p %s --sport %d  -j ACCEPT", cproto, sport);
#ifdef _DEBUG
	printf("In IptablesAllowOldApp:%s\n", iptablescmd);
#endif
	system(iptablescmd);
	//add rules on INPUT
	if ( dport_counter == 0 )
	{
		memset(iptablescmd, 0, 128);
		sprintf(iptablescmd, "iptables -I INPUT -p %s --sport %d  -j ACCEPT", cproto, dport);

#ifdef _DEBUG
		printf("In IptablesAllowOldApp_dport_counter==0:%s\n", iptablescmd);
#endif
		system(iptablescmd);
		allowdport[dport_counter].dport = dport;
		allowdport[dport_counter].protocol = protocol;
		allowdport[dport_counter].counter ++;
		dport_counter ++;
	}
	else
	{
		//check if dport exist
		for ( i = 0; i < dport_counter; i++ )
		{
			if ( dport == allowdport[i].dport && allowdport[i].protocol == protocol )
			{
				allowdport[i].counter ++;
				return;
			}
		}
		allowdport[dport_counter].dport = dport;
		allowdport[dport_counter].protocol = protocol;
		allowdport[dport_counter].counter ++;
		dport_counter ++;

		memset(iptablescmd, 0, 128);
		sprintf(iptablescmd, "iptables -I INPUT -p %s --sport %d   -j ACCEPT", cproto, dport);

#ifdef _DEBUG
		printf("In IptablesAllowOldApp_ADDNEWDPORT:%s\n", iptablescmd);
#endif
		system(iptablescmd);
	}

	//add by liuhui to open the app's listing port
	//some app such as ipmsg will listening some port,so we must deal with it
	// delete this code by liuhui, because it breaks our design
/*
	int listening_port[MAX_LISTENING];
	if ( app_conf[index].tcplink_counter == 0 )
	{
		int listening_sz = IptablesCheckListeningPort(pid, listening_port, MAX_LISTENING);
		if ( listening_sz == 0 )
		{
			printf("%s has no listening port\n", app_conf[index].name);
		}
		for ( i = 0; i < listening_sz; i++ )
		{
			IptablesOpenService(listening_port[i]);
		}
	}
	app_conf[index].tcplink_counter ++;
*/
}


/*
 *Function Name:IptablesDeleteSportRules
 *
 *Parameters:sport
 *
 *Description:we should call libiptc to delete rules becasue too much times calls for iptables
 *will make system slow 
 *but now we only can used iptables,maybe later i will find solution
 *
 *Returns:
 *
 */
void IptablesDeleteSportRules(int sport)
{
	char command[128];
	memset(command, 0, 128);
	sprintf(command, "iptables -D OUTPUT -p tcp --sport %d -j ACCEPT", sport);
#ifdef _DEBUG
	printf("In IptabesDeleteSportRules:\n%s", command);
#endif
	system(command);
}
